
#include "SDL_image.h"
#include "SDL_rotozoom.h"
#include "Surface.h"
#include "Misc.h"

namespace Engine {

namespace Synaptic {

std::map<std::string, SDL_Surface *> Surface::surfaceMap; // Keep track of every system surface
// Keep track of every system surface

SDL_Surface * Surface::getSurface(std::string fileName) throw(const char *)
{

	if (surfaceMap.find(fileName) == surfaceMap.end()) {
	  // loads the surface and put it into the map
		SDL_Surface * tmpSurface = IMG_Load(fileName.c_str());
		if (!tmpSurface) throw SDL_GetError();
		SDL_SetAlpha(tmpSurface, SDL_SRCALPHA | SDL_RLEACCEL, 0);
		if (SDL_GetVideoSurface() != NULL) {
			surfaceMap[fileName] = SDL_DisplayFormatAlpha(tmpSurface);
			SDL_FreeSurface(tmpSurface);
		} else { 
			surfaceMap[fileName] = tmpSurface;
		}
	}
	return surfaceMap[fileName];
}

void Surface::setSurface(std::string name, SDL_Surface * surface)
{
	surfaceMap[name] = surface;
}

void Surface::blit(const SDL_Surface * const source, const Misc::Frame & srcframe, SDL_Surface * destination, Misc::Frame & dstframe)
{
	SDL_Rect srcrect, dstrect;
	srcrect = srcframe;
	dstrect = dstframe;
	
	// prevent useless off-screen blitting
	if ((dstframe.x > destination->w) || (dstframe.y > destination->h) || (dstframe.x + srcframe.w < 0) || (dstframe.y + srcframe.h) < 0)
		return;
	
	SDL_BlitSurface(const_cast<SDL_Surface*>(source),(&srcrect),destination,&dstrect);
}

// Slice a surface in the indicated frame

SDL_Surface * Surface::slice(std::string from, const Misc::Frame & sliceFrame, std::string newName)
{

	SDL_Surface *tmpSurface, *source;
	source = surfaceMap[from];
	if (!source) return NULL;
	if ((tmpSurface = surfaceMap[newName]) != NULL) return tmpSurface;
	tmpSurface = create(source->flags, sliceFrame.w, sliceFrame.h);
	SDL_BlitSurface(source,(SDL_Rect *)&(sliceFrame),tmpSurface,NULL);
	SDL_SetAlpha(tmpSurface, SDL_RLEACCEL, 0);
	/* i don't like code duplication either. */
	if (SDL_GetVideoSurface() != NULL) {
		surfaceMap[newName] = SDL_DisplayFormatAlpha(tmpSurface);
		SDL_FreeSurface(tmpSurface);
	} else { 
		surfaceMap[newName] = tmpSurface;
	}
	return surfaceMap[newName];
}

SDL_Surface * Surface::resize(std::string sourceName, double xFactor, double yFactor, std::string newName)
{
	SDL_Surface *tmpSurface, *source;
	source = surfaceMap[sourceName];
	if (!source) return NULL;
	if ((tmpSurface = surfaceMap[newName]) != NULL) return tmpSurface;
	tmpSurface = rotozoomSurfaceXY(source, 0.0f, xFactor, xFactor, 1);
	SDL_SetAlpha(tmpSurface, SDL_SRCALPHA | SDL_RLEACCEL, 0);
	if (SDL_GetVideoSurface() != NULL) {
		surfaceMap[newName] = SDL_DisplayFormatAlpha(tmpSurface);
		SDL_FreeSurface(tmpSurface);
	} else {
		surfaceMap[newName] = tmpSurface;
	}
	return surfaceMap[newName];
}

SDL_Surface * Surface::rotate(std::string sourceName, double degree, std::string newName)
{
	SDL_Surface *tmpSurface, *source;
	source = surfaceMap[sourceName];
	if (!source) return NULL;
	if ((tmpSurface = surfaceMap[newName]) != NULL) return tmpSurface;
	tmpSurface = rotozoomSurfaceXY(source, degree, 1.0, 1.0, 1);
	SDL_SetAlpha(tmpSurface, SDL_SRCALPHA | SDL_RLEACCEL, 0);
	if (SDL_GetVideoSurface() != NULL) {
		surfaceMap[newName] = SDL_DisplayFormatAlpha(tmpSurface);
		SDL_FreeSurface(tmpSurface);
	} else {
		surfaceMap[newName] = tmpSurface;
	}
	return surfaceMap[newName];
}

// Creates a new surface with the given width and height and flags
SDL_Surface * Surface::create(Uint32 width, Uint32 height, Uint32 flags){
	SDL_Surface * tmpSurface = 0, * returnSurface = 0;
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
	tmpSurface = SDL_CreateRGBSurface(flags, width, height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
#else
	tmpSurface = SDL_CreateRGBSurface(flags, width, height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
#endif
	if (SDL_GetVideoSurface() != NULL) {
		returnSurface = SDL_DisplayFormatAlpha(tmpSurface);
		SDL_FreeSurface(tmpSurface);
	} else {
		returnSurface = tmpSurface;
	}
	return returnSurface;
}


} // namespace Engine::Synaptic

} // namespace Engine
